/**
 * 
 */
package co.com.unmasters.pokerbot.core;

import java.util.ArrayList;
import java.util.HashMap;

import co.com.unmasters.pokerbot.util.ApplicationUtil;
import co.com.unmasters.pokerbot.util.Logger;
import pruebas.ParaProbar;


/**
 * Clase que actua como dealer para inid 
 * @author crodriguez
 */
public class Dealer {
	public enum Juego {carta_alta_secundaria,carta_alta_principal,par,dos_pares,trio,full,poker,escalera_normal,color,escalera_color}

	//Factor de puntaje por valor de juego y carta:
	final static int PUNTOS_JUEGO = 10000;
	final static int PUNTOS_CARTA_JUEGO = 100;
	final static int PUNTOS_CARTA_ACOMPANANTE = 1;
		
    /**
     * Funci�n para repartir las cartas en la mesa
     * @param cartas Baraja de la Mesa
     * @param jugadores Jugadores
     * @author nanana
     */
    protected static final void repartirCartasJugadores(ArrayList<Carta> cartas, ArrayList<Jugador> jugadores) {
        Logger.println(Dealer.class, "##Repartiendo Cartas a cada Jugador", Logger.NIVEL_LOG_JUEGO);
        Logger.println(Dealer.class, "##Numero de Jugadores: " + jugadores.size(), Logger.NIVEL_LOG_DEBUG);
        Logger.println(Dealer.class, "##Cartas en Baraja: " + cartas.size(), Logger.NIVEL_LOG_DEBUG);
        ArrayList<Carta> cartasJugador = null;
        
        for (int i = 0; i < jugadores.size(); i++) {
            cartasJugador = new ArrayList<Carta>();
            cartasJugador.add(cartas.get(0));
            cartasJugador.add(cartas.get(1));
            cartas.remove(0);
            cartas.remove(0);
            
            //Asigna Cartas al Jugador
            jugadores.get(i).setCartas(cartasJugador);
            
            /**LOG*/
            Logger.println(Dealer.class, "###" + Torneo.participantes.get(jugadores.get(i).getId()).miNick() + "\t(" + cartasJugador.get(0) + " - " + cartasJugador.get(1) + ")", Logger.NIVEL_LOG_JUEGO);
        }
        
        Logger.println(Dealer.class, "##Cartas en Baraja: " + cartas.size(), Logger.NIVEL_LOG_DEBUG);
    }

    /**
     * Funci�n que retorna las cartas comunes
     * @param cartas Baraja de la mesa
     * @return Carats comunes
     * @author nanana
     */
    protected static final ArrayList<Carta> repartirCartasComunes(ArrayList<Carta> cartas, int estadoMesa ) {
        ArrayList<Carta> cartasComunes = new ArrayList<Carta>();
              
        if ( estadoMesa == Mesa.POSTFLOP ) {
			for ( int i = 0; i < 3; i++)
				cartasComunes.add(cartas.get(i));
			for (int i = 0; i < 3; i++)
				cartas.remove(0);
		} else if ( estadoMesa == Mesa.TURN ||  estadoMesa == Mesa.RIVER  ) {
			for ( int i = 0; i < 1; i++)
				cartasComunes.add(cartas.get(i));
			for (int i = 0; i < 1; i++)
				cartas.remove(0);
		}

        Logger.println(Dealer.class, "##CARTAS COM?NES: " + ApplicationUtil.cartasAsString(cartasComunes), Logger.NIVEL_LOG_JUEGO);
		/**LOG*/
        Logger.println(Dealer.class, "##Cartas en Baraja: " + cartas.size(), Logger.NIVEL_LOG_DEBUG);
        Logger.println(Dealer.class, "##FIN Cartas Com?nes: ", Logger.NIVEL_LOG_DEBUG);
        
        return cartasComunes;
    }
    
    /**
     * Funci�n que obtiene el ganador de la mesa
     * @param mesa
     * @return Jugador Ganador
     * @author crodriguez
     */
    protected static final Jugador ganador(Mesa mesa){
        ArrayList<Jugador> jugadores = mesa.getJugadores();
        HashMap<Jugador, Integer> jugadorPuntaje = new HashMap<Jugador, Integer>();
        
        for (Jugador jugador : jugadores) {
            if (jugador.getRespuesta() == mesa.getApuesta()  ) {
				int puntosAcumulado = 0;
				int puntos = 0;;
				for (int i = Juego.values().length - 1; i >= 0; i--) {
				    JuegoActual juegoActual = verificarJuego(Juego.values()[i],
			                            mesa.getCartasComunes(), jugador.getCartas());
					if (  juegoActual != null ) {
						puntos = juegoActual.getPuntajeTotal();
					}
					System.out.println( jugador.getId()+" "+puntos+" juego "+Juego.values()[i] );
					puntosAcumulado+=puntos;
					break; //TODO: Por revisar si aplica
				}
				
				Logger.println(Dealer.class, "##Jugador " + jugador.getNickParticipanteJugador() + " Puntos: " + puntosAcumulado, Logger.NIVEL_LOG_DEBUG);
				
				//if (puntosAcumulado > 0) {
					jugadorPuntaje.put(jugador, puntosAcumulado);
				//}
			}
        }


        return obtenerGanador(mesa, jugadorPuntaje);
    }

    /**
     * Funci�n que con base en el puntaje determina el ganador
     * @param mesa Mesa de Juego
     * @param jugadoresPuntaje Puntaje por jugador
     * @return Jugador Ganador
     * @author crodriguez
     */
    private static final Jugador obtenerGanador(Mesa mesa, HashMap<Jugador, Integer> jugadorPuntaje){
        int max = 0;
        Jugador jugadorGanador = null;
        ArrayList<Jugador> jugadores = mesa.getJugadores();
       System.out.println(jugadorPuntaje.values());
        for (Jugador jugador : jugadores) {
            if (jugador.getRespuesta() == mesa.getApuesta() && jugadorPuntaje.get(jugador)!= null ) {
				int puntaje = jugadorPuntaje.get(jugador);
				if (puntaje > max) {
					jugadorGanador = jugador;
					max  = puntaje;
				}
			}
        }
        
        return jugadorGanador;
    }
    
    /**
     * Funci�n que verifica de acuerdo al juego indicado
     * @param juego
     * @author crodriguez
     */
    private static final JuegoActual verificarJuego(Juego juego, final ArrayList<Carta> cartasMesa, final ArrayList<Carta> cartasJugador){
        ArrayList<Carta> cartas = new ArrayList<Carta>();
        cartas.addAll(cartasMesa);
        cartas.addAll(cartasJugador);
        
        ArrayList<Carta> cartasJuego = null;
        
        switch (juego) {
            case carta_alta_principal: 
                cartasJuego = cartaAlta(cartasJugador);
	            break;
            case par: 
                cartasJuego = tienePar(cartas);
                break;
            case dos_pares: 
                cartasJuego = tieneDosPares(cartas);
                break;
            case trio: 
                cartasJuego = tieneTrio(cartas);
                break;
            case full: 
                cartasJuego = tieneFull(cartas);
                break;
            case poker:  
                cartasJuego = tienePoker(cartas);
                break;
            case escalera_normal: 
                cartasJuego = tieneEscaleraNormal(cartas);
                break;
            case color: 
                cartasJuego = tieneColor(cartas);
                break;
            case escalera_color: 
                cartasJuego = tieneEscaleraColor(cartas);
                break;
        }
        
        // Si no hay juego devuelve nulo indicando que no se tiene juego
        if (cartasJuego==null || (cartasJuego!=null && cartasJuego.size()==0)){
            return null;
        }
        
        // Si existe Juego arma el juego actual 
        JuegoActual juegoJugador = new JuegoActual(juego, cartasJuego);
        return juegoJugador;
    }
    
    /**
     * Funci�n que verifica si en las cartas se tiene un para
     * @param cartas
     * @return Cartas Juegos de Pares
     * @author crodriguez
     */
    private static final ArrayList<Carta> tienePar(final ArrayList<Carta> cartas){
        int[] repeticiones = obtenerRepeticiones(cartas);
        ArrayList<Carta> cartasJuego = new ArrayList<Carta>();

        for (int i = 0; i < repeticiones.length; i++) {
            if (repeticiones[i]==2) cartasJuego.add(cartas.get(i));
        }
        
        if (cartasJuego.size()==2){
            return cartasJuego;
        }
        
        return null;
    }

    /**
     * Funci?n que verifica si en las cartas se tiene dos pares
     * @param cartas
     * @return Cartas que tienen el juego indicado
     * @author crodriguez
     */
    private static final  ArrayList<Carta> tieneDosPares(final ArrayList<Carta> cartas){
        int[] repeticiones = obtenerRepeticiones(cartas);
        ArrayList<Carta> cartasJuego = new ArrayList<Carta>();
        
        for (int i = 0; i < repeticiones.length; i++) {
            if (repeticiones[i]==2) cartasJuego.add(cartas.get(i));
        }
        
        if (cartasJuego.size()>2){
            return cartasJuego;
        }
        
        return null;
    }
    /**
     * Funci?n que verifica si en las cartas se tiene trio
     * @param cartas
     * @return Cartas del Juego
     * @author crodriguez
     */
    private static final ArrayList<Carta> tieneTrio(final ArrayList<Carta> cartas){
        int[] repeticiones = obtenerRepeticiones(cartas);
        ArrayList<Carta> cartasJuego = new ArrayList<Carta>();
        
        for (int i = 0; i < repeticiones.length; i++) {
            if (repeticiones[i]==3) cartasJuego.add(cartas.get(i));
        }
        
        return cartasJuego;
    }
    /**
     * Funci?n que verifica si en las cartas se tiene poker
     * @param cartas
     * @return Cartas del Juego
     * @author crodriguez
     */
    private static final ArrayList<Carta> tienePoker(final ArrayList<Carta> cartas){
        int[] repeticiones = obtenerRepeticiones(cartas);
        ArrayList<Carta> cartasJuego = new ArrayList<Carta>();

        for (int i = 0; i < repeticiones.length; i++) {
            if (repeticiones[i]==4) cartasJuego.add(cartas.get(i));
        }
        
        return cartasJuego;
    }
    
    /**
     * Funci?n que da el puntaje de acuerdo a la carta alta
     * @param cartas
     * @return
     */
    protected static final ArrayList<Carta> cartaAlta(final ArrayList<Carta> cartas){
        Carta cartaAlta = null;
        int puntosCartaAlta = 0;

        for (Carta carta : cartas) {
            //Para el caso del AS cuenta como 14
            int puntosCarta = (carta.getNumero()==0?14:carta.getNumero());
            
            if (puntosCarta>puntosCartaAlta){
                cartaAlta = carta;
                puntosCartaAlta = puntosCarta;
            }
        }
        
        ArrayList<Carta> cartasJuego = new ArrayList<Carta>();
        cartasJuego.add(cartaAlta);
        
        return cartasJuego;
    }
    
    /**
     * Retorna los puntos ganados por el juego obtenido
     * @param cartas
     * @return Puntos de Juego
     */
    private static final ArrayList<Carta> tieneFull(final ArrayList<Carta> cartas){
        int[] repeticiones = obtenerRepeticiones(cartas);
        ArrayList<Carta> cartasJuego = new ArrayList<Carta>();
        
        //Se verifica si existe el trio
        for (int i = 0; i < repeticiones.length; i++) {
            if (repeticiones[i]==3)cartasJuego.add(cartas.get(i));
        }
        
        //Si no se encuentra el trio se devuelve sin juego
        if (cartasJuego.size()!=3){
            return null;
        }
        
        //Se verifica si existe el par
        for (int i = 0; i < repeticiones.length; i++) {
            if (repeticiones[i]==2) cartasJuego.add(cartas.get(i));
            if (cartasJuego.size()==5) break;
        }

        // Si hay 5 cartas un trio y un par se devuelve el juego
        if(cartasJuego.size()==5 ){
        	return cartasJuego;
        }
        
        return null;
    }
    
    /**
     * Verifica si el participante tiene escalera y en caso de ser as? devuelve los puntos correspondientes
     * @param cartas
     * @return Cartas que componene el juego
     */
    private static final ArrayList<Carta> tieneEscaleraNormal(final ArrayList<Carta> cartas){
        //N?meros de cartas
        boolean[] cartasNumeros = new boolean [15];
        ArrayList<Carta> cartasJuego = new ArrayList<Carta>();

        //Establece el valor true en la carta
        for (Carta carta : cartas) {
            cartasNumeros[carta.getNumero()]=true;
        }
        
        //La posici?n 15 es igual a la primera dado que puede comenzar con AS o terminar 
        cartasNumeros[14]=cartasNumeros[0];
        
        //Verificar si se tiene 5 trues seguidos en caso de ser as? hay escalera!!
        int index = -1;
        int escalera = 0;
        
        for (int i = 0; i < cartasNumeros.length; i++) {
            escalera = (cartasNumeros[i]?escalera+1:0);  
            
            if (escalera==5){
                index = i;
                break;
            }
        }
        
        //No hay escalera
        if (index==-1){
            return null;
        }
        
        //Obtener las cartas que componen la escalera
        for (int i = 0; i < 5; i++) {
            for (Carta carta : cartasJuego) {
                if (carta.getNumero()==index || (index==14 && carta.getNumero()==1)){
                    cartasJuego.add(carta);
                    index--;
                    break;
                }
            }
        }
        
        
        return cartasJuego;
    }
    
    /**
     * Verifica si el participante tiene escalera y en caso de ser as? devuelve los puntos correspondientes
     * @param cartas
     * @return
     */
    private static final ArrayList<Carta> tieneEscaleraColor(final ArrayList<Carta> cartas){
        //N?meros de cartas
        boolean [][]cartasPalos = new boolean [4][15];
        ArrayList<Carta> cartasJuego = new ArrayList<Carta>();

        //Establece el valor true en la carta
        for (Carta carta : cartas) {
            cartasPalos[carta.getPalo()][carta.getNumero()] = true;
        }
        
        //La posici?n 15 es igual a la primera dado que puede comenzar con AS o terminar 
        for (int i = 0; i < 4; i++) {
            cartasPalos[i][14]=cartasPalos[i][0];    
        }
        
        int palo = -1;
        int index = -1;
        //Verificar si se tiene 5 trues seguidos para cada palo en caso de ser as? hay escalera!!
        palos: for (int i = 0; i < cartasPalos.length; i++) {
            boolean []cartasPalo = cartasPalos[i];
            
            int escalera = 0;
            
            for (int j = 0; j < cartasPalo.length; j++) {
                escalera = (cartasPalo[j]?escalera+1:0);  
                
                if (escalera==5){
                    palo = i;
                    index = j;
                    break palos;
                }
            }
        }
        
        //No hay escalera
        if (index==-1 || palo==-1){
            return null;
        }
        
        //Obtener las cartas que componen la escalera
        for (int i = 0; i < 5; i++) {
            for (Carta carta : cartasJuego) {
                if ((carta.getNumero()==index || (index==14 && carta.getNumero()==1)) && (carta.getPalo()==palo)){
                    cartasJuego.add(carta);
                    index--;
                    break;
                }
            }
        }
        
        return null;
    }
    
    /**
     * Verifica si el participante tiene color
     * @param cartas
     * @return Cartas del Juego
     */
    private static final ArrayList<Carta> tieneColor(final ArrayList<Carta> cartas){
        int palo = -1;
        int[] palos = new int[4];
        ArrayList<Carta> cartasJuego = new ArrayList<Carta>();
        
        for (Carta carta : cartas) {
            palos[carta.getPalo()]++;
            
            if (palos[carta.getPalo()]>=5){
                palo = carta.getPalo(); 
            }
        }
        
        //Si no hay palo con color
        if (palo==-1){
            return null;
        }

        //Obtiene las cartas que tengan el mismo palo
        for (Carta carta : cartas) {
            if (palo==carta.getPalo()) cartasJuego.add(carta);
            if (cartasJuego.size()==5) break;
        }
        
        return cartasJuego;
    }
    
    /**
     * De acuerdo a las cartas entregadas verifica cuantas veces se repite en la baraja
     * @param cartas Cartas
     * @param tipoRepetici?n (N)?mero o (P)alo
     * @return Arreglo con el n�mero de repeticiones de la carta
     * @author crodriguez
     */
    public static final int[] obtenerRepeticiones (ArrayList<Carta> cartas){
        int [] repeticiones = new int[cartas.size()];
        
        // Se toman en orden las cartas y se comparan con las demas
        for (int i = 0; i < cartas.size(); i++) {
            Carta cartaBase = cartas.get(i);
            
            for (int j = i; j < cartas.size(); j++) {
                Carta cartaComparar = cartas.get(j);
                
                if (cartaBase.getNumero()==cartaComparar.getNumero()){
                    repeticiones[i]++;
                    if ( i!= j){
                    	repeticiones[j]++;
                    }
                }
            }
        }
        
        return repeticiones;
    }
}